import js from '@eslint/js';
import typescript from '@typescript-eslint/eslint-plugin';
import typescriptParser from '@typescript-eslint/parser';

/**
 * Custom ESLint rule: structured-logging
 *
 * Enforces structured logging format: logger.level({ src: '<source>', ...context }, 'message')
 *
 * @see LOGGING_SPEC.md for full specification
 */
const LOGGER_METHODS = ['debug', 'info', 'warn', 'error', 'trace', 'fatal'];

const structuredLoggingRule = {
  meta: {
    type: 'problem',
    docs: {
      description: 'Enforce structured logging format with src property',
      category: 'Best Practices',
      recommended: true,
    },
    fixable: null,
    schema: [],
    messages: {
      missingObjectArg:
        'Logger calls must have an object as first argument: logger.{{method}}({ src: "...", ...context }, "message")',
      missingSrcProperty:
        'Logger calls must include "src" property: logger.{{method}}({ src: "...", ...context }, "message")',
      srcMustBeString: 'The "src" property must be a string literal',
      missingMessageArg:
        'Logger calls must have a string message as second argument: logger.{{method}}({ src: "...", ...context }, "message")',
      messageMustBeString: 'Second argument to logger must be a string message',
      invalidSrcFormat:
        'Invalid src format "{{src}}". Use: "component" (core), "plugin:name:type:component" (plugins), or "http" (server)',
    },
  },
  create(context) {
    // Very permissive: just check src is not empty and has reasonable format
    // Format examples: "http", "cli", "plugin:bootstrap:action:reply", "core:runtime", etc.
    const srcRegex = /^[a-z][a-z0-9_:-]*$/i;

    function isLoggerCall(node) {
      return (
        node.type === 'CallExpression' &&
        node.callee.type === 'MemberExpression' &&
        node.callee.object.type === 'Identifier' &&
        node.callee.object.name === 'logger' &&
        node.callee.property.type === 'Identifier' &&
        LOGGER_METHODS.includes(node.callee.property.name)
      );
    }

    function validateSrcFormat(srcValue) {
      return srcRegex.test(srcValue);
    }

    return {
      CallExpression(node) {
        if (!isLoggerCall(node)) {
          return;
        }

        const methodName = node.callee.property.name;
        const args = node.arguments;

        if (args.length === 0) {
          context.report({ node, messageId: 'missingObjectArg', data: { method: methodName } });
          return;
        }

        const firstArg = args[0];

        // First argument must be an object literal
        if (firstArg.type !== 'ObjectExpression') {
          if (
            firstArg.type === 'Literal' ||
            firstArg.type === 'TemplateLiteral' ||
            firstArg.type === 'BinaryExpression'
          ) {
            context.report({ node, messageId: 'missingObjectArg', data: { method: methodName } });
          }
          return;
        }

        // Check for src property
        const srcProperty = firstArg.properties.find(
          (prop) =>
            prop.type === 'Property' &&
            !prop.computed &&
            ((prop.key.type === 'Identifier' && prop.key.name === 'src') ||
              (prop.key.type === 'Literal' && prop.key.value === 'src'))
        );

        if (!srcProperty) {
          context.report({
            node: firstArg,
            messageId: 'missingSrcProperty',
            data: { method: methodName },
          });
          return;
        }

        // src must be a string literal
        if (srcProperty.value.type !== 'Literal' || typeof srcProperty.value.value !== 'string') {
          context.report({ node: srcProperty.value, messageId: 'srcMustBeString' });
          return;
        }

        // Validate src format
        const srcValue = srcProperty.value.value;
        if (!validateSrcFormat(srcValue)) {
          context.report({
            node: srcProperty.value,
            messageId: 'invalidSrcFormat',
            data: { src: srcValue },
          });
        }

        // Check second argument (message)
        if (args.length < 2) {
          context.report({ node, messageId: 'missingMessageArg', data: { method: methodName } });
          return;
        }

        const secondArg = args[1];
        if (secondArg.type === 'ObjectExpression' || secondArg.type === 'ArrayExpression') {
          context.report({ node: secondArg, messageId: 'messageMustBeString' });
        }
      },
    };
  },
};

/**
 * ElizaOS custom ESLint plugin
 */
const elizaPlugin = {
  meta: { name: '@elizaos/eslint-plugin', version: '1.0.0' },
  rules: {
    'structured-logging': structuredLoggingRule,
  },
};

/**
 * Base ESLint configuration for ElizaOS packages
 * Provides consistent code quality across all packages
 */
export const baseConfig = [
  js.configs.recommended,
  {
    files: ['**/*.{js,mjs,cjs,ts,tsx}'],
    languageOptions: {
      parser: typescriptParser,
      parserOptions: {
        ecmaVersion: 2022,
        sourceType: 'module',
        ecmaFeatures: {
          jsx: true,
        },
      },
      globals: {
        console: 'readonly',
        process: 'readonly',
        Buffer: 'readonly',
        __dirname: 'readonly',
        __filename: 'readonly',
        module: 'readonly',
        require: 'readonly',
        global: 'readonly',
        globalThis: 'readonly',
        exports: 'writable',
        setTimeout: 'readonly',
        clearTimeout: 'readonly',
        setInterval: 'readonly',
        clearInterval: 'readonly',
        setImmediate: 'readonly',
        clearImmediate: 'readonly',
        FormData: 'readonly',
        File: 'readonly',
        Blob: 'readonly',
        URL: 'readonly',
        URLSearchParams: 'readonly',
        ArrayBuffer: 'readonly',
        Uint8Array: 'readonly',
        Float32Array: 'readonly',
        fetch: 'readonly',
        performance: 'readonly',
        AbortController: 'readonly',
        AbortSignal: 'readonly',
        NodeJS: 'readonly',
        btoa: 'readonly',
        atob: 'readonly',
        Bun: 'readonly',
        Response: 'readonly',
        BufferEncoding: 'readonly',
      },
    },
    plugins: {
      '@typescript-eslint': typescript,
      '@elizaos': elizaPlugin,
    },
    rules: {
      // ElizaOS custom rules - structured logging
      '@elizaos/structured-logging': 'warn',

      // TypeScript specific rules - balanced for maintainability
      '@typescript-eslint/no-unused-vars': [
        'warn',
        { argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' },
      ],
      '@typescript-eslint/no-explicit-any': 'off',
      '@typescript-eslint/explicit-function-return-type': 'off',
      '@typescript-eslint/explicit-module-boundary-types': 'off',
      '@typescript-eslint/no-inferrable-types': 'off',
      '@typescript-eslint/no-non-null-assertion': 'off',
      '@typescript-eslint/ban-ts-comment': 'warn',

      // General JavaScript/TypeScript rules
      'no-unused-vars': 'off',
      'no-console': 'off',
      'no-debugger': 'error',
      'no-alert': 'warn',
      'no-var': 'error',
      'prefer-const': 'warn',
      'prefer-arrow-callback': 'error',
      'arrow-spacing': 'error',
      'object-shorthand': 'error',
      'prefer-template': 'error',
      'template-curly-spacing': 'error',
      'no-multiple-empty-lines': ['error', { max: 2, maxEOF: 1 }],
      'eol-last': 'error',
      'comma-dangle': ['error', 'only-multiline'],
      semi: ['error', 'always'],
      quotes: ['error', 'single', { avoidEscape: true }],
      indent: ['error', 2, { SwitchCase: 1 }],
      'no-trailing-spaces': 'error',
      'keyword-spacing': 'error',
      'space-before-blocks': 'error',
      'object-curly-spacing': ['error', 'always'],
      'array-bracket-spacing': ['error', 'never'],
      'computed-property-spacing': ['error', 'never'],
      'space-in-parens': ['error', 'never'],
      'space-before-function-paren': [
        'error',
        {
          anonymous: 'always',
          named: 'never',
          asyncArrow: 'always',
        },
      ],

      // Best practices
      eqeqeq: ['error', 'always'],
      curly: ['error', 'all'],
      'no-eval': 'error',
      'no-implied-eval': 'error',
      'no-new-func': 'error',
      'no-return-assign': 'error',
      'no-self-compare': 'error',
      'no-sequences': 'error',
      'no-throw-literal': 'error',
      'no-unmodified-loop-condition': 'error',
      'no-unused-expressions': 'error',
      'no-useless-call': 'error',
      'no-useless-concat': 'error',
      'no-useless-return': 'error',
      'prefer-promise-reject-errors': 'error',
      radix: 'warn',
      yoda: 'warn',

      // Relaxed rules for complex codebase
      'no-duplicate-imports': 'off',
      'no-useless-catch': 'warn',
      'no-fallthrough': 'warn',
      'no-case-declarations': 'off',
      'no-control-regex': 'warn',
      'no-useless-escape': 'off',
      'no-empty': 'warn',
      'no-unreachable': 'warn',
      'no-undef': 'off', // TypeScript handles this
    },
  },
];

export const testOverrides = {
  files: [
    '**/*.test.{js,ts,tsx}',
    '**/*.spec.{js,ts,tsx}',
    '**/__tests__/**/*',
    '**/test-utils/**/*',
    '**/tests/**/*',
  ],
  languageOptions: {
    globals: {
      describe: 'readonly',
      it: 'readonly',
      test: 'readonly',
      expect: 'readonly',
      beforeEach: 'readonly',
      afterEach: 'readonly',
      beforeAll: 'readonly',
      afterAll: 'readonly',
      jest: 'readonly',
      vitest: 'readonly',
      mock: 'readonly',
    },
  },
  rules: {
    '@elizaos/structured-logging': 'off', // Tests can use any log format
    '@typescript-eslint/no-explicit-any': 'off',
    '@typescript-eslint/no-unused-vars': 'off',
    '@typescript-eslint/no-non-null-assertion': 'off',
    'no-console': 'off',
    'no-undef': 'off',
    'no-duplicate-imports': 'off',
    'no-useless-catch': 'off',
    'no-fallthrough': 'off',
    'no-case-declarations': 'off',
    'no-control-regex': 'off',
    'no-useless-escape': 'off',
  },
};

export const standardIgnores = [
  // Dependencies and package managers
  '**/node_modules/',
  'node_modules/**',
  '.pnp',
  '.pnp.js',
  'package-lock.json',
  'yarn.lock',
  'pnpm-lock.yaml',
  'bun.lockb',

  // Build outputs and generated files
  '**/dist/',
  '**/build/',
  '**/out/',
  '**/.next/',
  '**/.nuxt/',
  '**/.output/',
  '**/coverage/',
  '**/.turbo/',
  '**/.cache/',
  '**/.temp/',
  '**/.tmp/',
  '**/tmp/',
  '**/temp/',
  '*.tsbuildinfo',
  '**/*.tsbuildinfo',

  // Minified and compiled files
  '**/*.min.js',
  '**/*.min.css',
  '**/*.bundle.js',
  '**/*.chunk.js',
  '**/*.map',

  // Static assets and public directories
  '**/public/',
  '**/static/',
  '**/assets/',

  // Environment and config files
  '.env*',
  '!.env.example',
  '**/*.config.js',
  '**/*.config.ts',
  '**/*.config.mjs',
  '**/*.config.cjs',
  '**/vitest.config.*',
  '**/vite.config.*',
  '**/playwright.config.*',
  '**/cypress.config.*',
  '**/tailwind.config.*',
  '**/postcss.config.*',
  '**/jest.config.*',
  '**/babel.config.*',
  '**/webpack.config.*',
  '**/rollup.config.*',
  '**/tsconfig*.json',

  // IDE and editor files
  '**/.vscode/',
  '**/.idea/',
  '**/*.swp',
  '**/*.swo',
  '**/.vim/',

  // OS files
  '**/.DS_Store',
  '**/Thumbs.db',
  '**/desktop.ini',

  // Version control
  '**/.git/',
  '**/.svn/',
  '**/.hg/',

  // Logs and debugging
  '**/*.log',
  '**/logs/',
  '**/.nyc_output/',

  // Test artifacts
  '**/cypress/screenshots/',
  '**/cypress/videos/',
  '**/test-results/',
  '**/playwright-report/',
  '**/__screenshots__/',

  // Database files
  '**/*.db',
  '**/*.sqlite',
  '**/*.sqlite3',

  // Archive files
  '**/*.zip',
  '**/*.tar.gz',
  '**/*.rar',
  '**/*.7z',

  // Binary files
  '**/*.exe',
  '**/*.dll',
  '**/*.so',
  '**/*.dylib',
  '**/*.bin',

  // ElizaOS specific ignores
  '**/.eliza*',
  '**/.eliza*/**',
  '**/:memory:*',
  '**/:memory:*/**',
  '**/training_recording*',
  '**/training_recording*/**',
  '**/research_logs',
  '**/research_logs/**',
  '**/verification-snapshots',
  '**/verification-snapshots/**',
  '**/.claude',
  '**/.claude/**',
  '**/.cursor',
  '**/.cursor/**',
  '**/.husky',
  '**/.husky/**',
  '**/.devcontainer',
  '**/.devcontainer/**',
  '**/.github',
  '**/.github/**',
  '**/scenarios',
  '**/scenarios/**',
  '**/archives',
  '**/archives/**',
  '**/data',
  '**/data/**',
  '**/test-data',
  '**/test-data/**',
  '**/plugin-data',
  '**/plugin-data/**',
  '**/enhanced-plugin-data',
  '**/enhanced-plugin-data/**',
  '**/plugin-fixes',
  '**/plugin-fixes/**',
  '**/temp',
  '**/temp/**',
  '**/plans',
  '**/plans/**',
  '**/world',
  '**/world/**',

  // Documentation and large files
  'CLAUDE.md',
  'AGENTS.md',
  'CHANGELOG.md',
  'CODE_OF_CONDUCT.md',
  'llms.txt',
  '*.postman.json',
  'ARCHITECTURAL_REVIEW_*.md',
  'IMPLEMENTATION_REPORT*.md',
  'FINAL_*.md',
  'PRODUCTION_STATUS.md',
  'BENCHMARK_RESULTS.md',
  'CRITICAL_REVIEW.md',
  'FRAMEWORK_ISSUES_ANALYSIS.md',
  '**/visual-test-screenshots',
  '**/visual-test-screenshots/**',
  '**/screenshots',
  '**/screenshots/**',
  '**/images',
  '**/images/**',
  '**/benchmarks',
  '**/benchmarks/**',
  '**/benchmark_results',
  '**/benchmark_results/**',
  '**/results',
  '**/results/**',
  '**/multi-swe-bench',
  '**/multi-swe-bench/**',
  '**/deep_research_bench',
  '**/deep_research_bench/**',
  '**/cypress',
  '**/cypress/**',
  '**/gazebo',
  '**/gazebo/**',
  '**/urdf',
  '**/urdf/**',
  '**/hyperfy-ts',
  '**/hyperfy-ts/**',
  '**/docs',
  '**/docs/**',
  '**/README.md',
  '**/*.md',
  '!src/**/*.md',

  // Documentation build outputs
  '**/docs/.vitepress/dist/',
  '**/docs/.vitepress/cache/',
  '**/storybook-static/',

  // Docker and container files
  'Dockerfile*',
  'docker-compose*.yml',
  'docker-compose*.yaml',
  '.dockerignore',

  // Additional directories
  '**/packages/**/*.md',
  '**/scripts',
  '**/scripts/**',
  '**/templates',
  '**/templates/**',
  '**/examples',
  '**/examples/**',
  '**/mocks',
  '**/mocks/**',
  '**/__mocks__',
  '**/__mocks__/**',
  '**/docker',
  '**/docker/**',
  '**/src-tauri',
  '**/src-tauri/**',
  '**/components.json',
  '**/bunfig.toml',
  '**/analyze-errors.cjs',
  '**/debug-*.js',
  '**/*.debug.js',
  '**/*.config.json',

  // Misc
  '**/.*rc.js',
  '**/.*rc.json',
  '**/.*rc.yml',
  '**/.*rc.yaml',
];
